import { Precision } from "../Precision";
import { Vector3 } from "../vector/Vector3";
import { XYZ } from "../coord/XYZ";

/**
 * @description 三维空间坐标点
 * @author xuld
 * @date 2024-11-08
 * @lastEditTime 2024-11-08 17:59:53
 * @lastEditors xuld
 */
export class Point3 {
    private _coord: XYZ;

    /**
     * @description 创建三维点
     * @param x - X坐标值，默认为0
     * @param y - Y坐标值，默认为0
     * @param z - Z坐标值，默认为0
     */
    constructor(x: number = 0, y: number = 0, z: number = 0) {
        this._coord = new XYZ(x, y, z);
    }

    /**
     * @description 获取X坐标值
     */
    public get x(): number {
        return this._coord.x;
    }

    /**
     * @description 设置X坐标值
     */
    public set x(value: number) {
        this._coord.x = value;
    }

    /**
     * @description 获取Y坐标值
     */
    public get y(): number {
        return this._coord.y;
    }

    /**
     * @description 设置Y坐标值
     */
    public set y(value: number) {
        this._coord.y = value;
    }

    /**
     * @description 获取Z坐标值
     */
    public get z(): number {
        return this._coord.z;
    }

    /**
     * @description 设置Z坐标值
     */
    public set z(value: number) {
        this._coord.z = value;
    }

    /**
     * @description 获取内部坐标对象
     */
    public get coord(): XYZ {
        return this._coord;
    }

    /**
     * @description 设置内部坐标对象
     */
    public set coord(value: XYZ) {
        this._coord = value;
    }

    /**
     * @description 判断两点是否相等
     * @param other - 要比较的点
     * @param tolerance - 容差值，默认为 EPSILON4
     * @returns 是否相等
     */
    public equals(other: Point3, tolerance: number = Precision.EPSILON4): boolean {
        return this._coord.equals(other._coord, tolerance);
    }

    /**
     * @description 计算两点之间的距离
     * @param other - 另一个点
     * @returns 距离值
     */
    public distance(other: Point3): number {
        return this._coord.distanceTo(other._coord);
    }

    /**
     * @description 计算两点之间距离的平方
     * @param other - 另一个点
     * @returns 距离的平方
     */
    public distanceSquare(other: Point3): number {
        return this._coord.subtract(other._coord).squareModulus();
    }

    /**
     * @description 从其他点复制坐标值
     * @param other - 要复制的点
     */
    public copyFrom(other: Point3): void {
        this._coord.copyFrom(other._coord);
    }

    /**
     * @description 计算从当前点到目标点的向量
     * @param pt - 目标点
     * @returns 方向向量
     */
    public vectorTo(pt: Point3): Vector3 {
        return new Vector3(
            pt.x - this._coord.x,
            pt.y - this._coord.y,
            pt.z - this._coord.z
        );
    }

    /**
     * @description 创建当前点的副本
     * @returns 新的点对象
     */
    public clone(): Point3 {
        return new Point3(this.x, this.y, this.z);
    }

    /**
     * @description 计算两点之间的差值，返回新点
     * @param other - 要相减的点
     * @returns 相减后的新点
     */
    public subtract(other: Point3): Point3 {
        return new Point3(this.x - other.x, this.y - other.y, this.z - other.z);
    }

    /**
     * @description 计算两点的点积
     * @param other - 另一个点
     * @returns 点积结果
     */
    public dot(other: Point3): number {
        return this._coord.dot(other._coord);
    }

    /**
     * @description 转换为字符串表示
     * @returns 格式化的点字符串
     */
    public toString(): string {
        return `{"type": "Point3", "coord": ${this._coord.toString()}}`;
    }

    /**
     * @description 根据向量移动到新的点位，返回新的结果对象
     * @param vec - 位移向量
     * @returns 新的点对象
     */
    public moveBy(vec: Vector3): Point3 {
        let ret = this.clone();
        ret.moveSelfBy(vec);
        return ret;
    }

    /**
     * @description 根据向量移动到新的点位，直接修改自身的值
     * @param vec - 位移向量
     */
    public moveSelfBy(vec: Vector3): void {
        this.coord.addSelf(vec.coord);
    }

    /**
     * @description 验证参数是否为有效数字
     * @throws 当参数无效时抛出错误
     */
    private validateNumbers(...args: number[]): void {
        for (const arg of args) {
            if (typeof arg !== 'number' || isNaN(arg) || !isFinite(arg)) {
                throw new Error('Invalid number parameter');
            }
        }
    }

    /**
     * @description 点绕轴旋转，返回新的点
     */
    public rotate(axis: Vector3, radian: number, center?: Point3): Point3 {
        if (!axis) {
            throw new Error("Rotation axis cannot be null");
        }

        if (axis.coord.isZero()) {
            throw new Error("Rotation axis cannot be zero vector");
        }

        this.validateNumbers(radian);  // 使用Point3自己的validateNumbers

        let newPoint = this.clone();
        newPoint.rotateSelf(axis, radian, center);
        return newPoint;
    }

    /**
     * @description 点自身绕轴旋转
     */
    public rotateSelf(axis: Vector3, radian: number, center?: Point3): void {
        if (!axis) {
            throw new Error("Rotation axis cannot be null");
        }

        if (axis.coord.isZero()) {
            throw new Error("Rotation axis cannot be zero vector");
        }

        this.validateNumbers(radian);  // 使用Point3自己的validateNumbers

        if (Math.abs(radian) < Precision.EPSILON10) {
            return;
        }

        // 如果有中心点，先将点移动到原点
        if (center) {
            this.x -= center.x;
            this.y -= center.y;
            this.z -= center.z;
        }

        // 确保旋转轴是单位向量
        const normalizedAxis = axis.normalize();

        // 使用Rodrigues旋转公式
        const cos = Math.cos(radian);
        const sin = Math.sin(radian);
        const dot = this.x * normalizedAxis.x + this.y * normalizedAxis.y + this.z * normalizedAxis.z;

        const newX = this.x * cos + (normalizedAxis.y * this.z - normalizedAxis.z * this.y) * sin +
            normalizedAxis.x * dot * (1 - cos);
        const newY = this.y * cos + (normalizedAxis.z * this.x - normalizedAxis.x * this.z) * sin +
            normalizedAxis.y * dot * (1 - cos);
        const newZ = this.z * cos + (normalizedAxis.x * this.y - normalizedAxis.y * this.x) * sin +
            normalizedAxis.z * dot * (1 - cos);

        this.x = newX;
        this.y = newY;
        this.z = newZ;

        // 如果有中心点，将点移回原位
        if (center) {
            this.x += center.x;
            this.y += center.y;
            this.z += center.z;
        }
    }
}